---@class TBS.CommandBase
local CommandBase = TBS.CreateClass("CommandBase")
function CommandBase:ctor()
    self:OnCreate()
end
function CommandBase:OnCreate()
    self.cid = 0
    self.frame = 0
    self.action = 0
    self.type = 0
    self.isExecute = false  --执行节点
    self.activeCallback = nil
    self.isActive = false       --节点激活
    self.isEnd = false  --节点执行完
end
function CommandBase:OnDestroy()
    TBS.Battle.RemoveDelayCall(self.delayTimerId)
end
function CommandBase:ToString()
    return "frame:"..self.frame..",cid:"..self.cid..",action:"..self.action..",type:"..self.type
end
function CommandBase:DelayCall(callback,time)
    self.delayTimerId = TBS.Battle.DelayCall(time/1000,callback,self)
    --self.delayTimerId = utimer.delayCall(callback,time,self)
end
function CommandBase:Reset()
    self.isExecute = false
    self.isActive = false
    self.isEnd = false
    TBS.Battle.RemoveDelayCall(self.delayTimerId)
    self.delayTimerId = nil
end
---@param preCmd TBS.CommandBase
function CommandBase:RegisterPreCmdActive(preCmd,callback)
    if preCmd then
        preCmd:RegisterActiveCallback(callback,self)
    else
        callback(self)
    end
end
---@private
function CommandBase:RegisterActiveCallback(callback, tb)
    if self.isActive then
        callback(tb)
        return
    end
    if self.activeCallback == nil then self.activeCallback = {} end
    for k,v in pairs(self.activeCallback) do
        if v.callback == callback and v.tb == tb then
            return
        end
    end
    table.insert(self.activeCallback,{ callback = callback , tb = tb})
end
function CommandBase:ActiveCall()
    if self.activeCallback == nil then return end
    for k,v in pairs(self.activeCallback) do
        v.callback(v.tb)
    end
end
function CommandBase:Execute()
    if self.isExecute then return end
    self.isExecute = true
    self:DoExecute()
end
function CommandBase:DoExecute()

end
function CommandBase:CmdEnd()
    self.isEnd = true
end
function CommandBase:Active()
    self.isActive = true
    self:ActiveCall()
end
---@class TBS.CommandInit: TBS.CommandBase
---@field New fun():TBS.CommandInit
local CommandInit = TBS.CreateClass("CommandInit","CommandBase")
function CommandInit:OnCreate()
    TBS.CommandBase.OnCreate(self)
    self.fighterId = 0
    self.modelId = 0
    self.maxHP = 0
    self.hp = 0
    self.actorType = 0
    self.groupId = 0
    self.groupIndex = 0
    self.playerName = ""
    self.playerId = ""
    self.level = 0
end
function CommandInit:ToString()
    local b = TBS.CommandBase.ToString(self)
    return "[初始化]  "..b..",fighterId:"..self.fighterId..",modelId:"..self.modelId..
            ",hp:"..self.hp..",actorType:"..self.actorType..",groupId:"..self.groupId..
            ",groupIndex:"..self.groupIndex..",playerName:"..self.playerName..",level:"..self.level
end
function CommandInit:DoExecute()
    local p = TBS.ActorMgr.CreatePlayer(self.fighterId,self.modelId,self.actorType,self.fighterId)
    local ActorType = TBS.Config.ActorType
    if self.actorType == ActorType.player then
        p:SetGroup(self.groupId,self.groupIndex)
        p:SetPos(TBS.Battle.GetGroupPosition(self.groupId,self.groupIndex))
        TBS.ActorMgr.AddGroup(self.groupId,self.groupIndex,p)
    elseif self.actorType == ActorType.leader then
        p:SetGroup(self.groupId,0)
        p:SetPos(TBS.Battle.GetLeaderPosition(self.groupId))
        p.name = self.playerName
        p.playerId = self.playerId
        p.level = self.level
        TBS.ActorMgr.AddLeader(self.groupId,p)
    elseif self.actorType == ActorType.boss then
        p:SetGroup(TBS.Battle.BOSS_GROUP,0)
        p:SetPos(TBS.Battle.GetBossPosition())
        p.level = self.level
        TBS.ActorMgr.AddBoss(p)
    end
    p:SetHp(self.hp)
    p:SetHpMax(self.maxHP)
end
---@class TBS.CommandAttack: TBS.CommandBase
---@field New fun():TBS.CommandAttack
local CommandAttack = TBS.CreateClass("CommandAttack","CommandBase")
function CommandAttack:OnCreate()
    TBS.CommandBase.OnCreate(self)
    self.fighterId = 0
    self.skillId = 0
    self.skillUniqueId = 0 --唯一性id
    self.sourceSkillUniqueId = 0 --唯一性id
    self.delayActive = 0
end
function CommandAttack:ToString()
    local b = TBS.CommandBase.ToString(self)
    return "    [攻击]  "..b..",fighterId:"..self.fighterId..",技能id:"..self.skillId..
            ",技能唯一id:"..self.skillUniqueId..",来源技能唯一id:"..self.sourceSkillUniqueId
end
function CommandAttack:DoExecute()
    local fighter = TBS.ActorMgr.GetActor(self.fighterId)
    if not fighter then
        logError("找不到该fighter："..self.fighterId)
        return
    end
    if self.sourceSkillUniqueId <= 0 then --触发时机查找上一次damage,attack
        fighter:CreateSkill(self.skillId,self.skillUniqueId)
        local node = self
        local preCmd
        while(node) do
            local cmd = TBS.ReportMgr.GetPreCmd(node)
            if cmd == nil then
                break
            end
            if self:HandleSpecialParam(cmd) then
                return
            end
            if cmd.action == TBS.Config.FightActionType.ATTACK and cmd.sourceSkillUniqueId ~= -1 then
                preCmd = cmd
                break
            elseif cmd.action == TBS.Config.FightActionType.DAMAGE and cmd.skillUniqueId ~= -1 then
                local skillCmd = TBS.ReportMgr.GetPreSkillCmd(cmd,cmd.skillUniqueId)
                if skillCmd and skillCmd.sourceSkillUniqueId ~= -1 then
                    preCmd = cmd
                    break
                end
            end
            node = cmd
        end
        self:RegisterPreCmdActive(preCmd,self.PreActiveCallback)
    else
        local sourceSkill = fighter:GetSkill(self.sourceSkillUniqueId)
        if sourceSkill == nil then
            logError(self.cid.." 找不到该sourceSkill："..self.sourceSkillUniqueId)
            return
        end
        local cfg = dataTable.getDataTableByStringId("t_collocationskill",sourceSkill.skillId)
        if cfg.f_OtherSkill ~= "" then
            local index = sourceSkill:GetSubSkillIndex()
            sourceSkill:InCreaseSubSkillIndex()
            local subData = cfg.f_OtherSkill[index]
            if subData == nil then
                self:Active()
                self:CmdEnd()
                logError("子技能触发次数有误skillId, "..sourceSkill.skillId..":"..#cfg.f_OtherSkill.."<"..index)
                return
            end
            local delay = subData[1]
            fighter:CreateSkill(self.skillId,self.skillUniqueId)
            if index ~= 1 then
                self.delayActive = delay - cfg.f_OtherSkill[1][1]
            end
            if sourceSkill.hasFrameEvent then--需要同步帧事件的时间
                if index ~= 1 then
                    self.delayActive = delay - cfg.f_OtherSkill[1][1]
                end
                sourceSkill:RegisterCallback(TBS.Config.SkillCallbackType.attackFrameEventCallback,self,self.PreActiveCallback,true)
            else
                self.delayActive = delay
                self:PreActiveCallback()
            end
        end
    end
end
--需特殊处理的参数
---@param preCmd TBS.CommandBase
function CommandAttack:HandleSpecialParam(preCmd)
    local cfg = dataTable.getDataTableByStringId("t_collocationskillshow",self.skillId)
    if string.IsNilOrEmpty(cfg.f_AttackParam) then
        return false
    end
    local _type = cfg.f_AttackParam[1]
    local param = cfg.f_AttackParam[2]
    if _type == TBS.Config.AttackParamType.delayAttack and preCmd.action == TBS.Config.FightActionType.ATTACK then
        self.delayActive = param
        self:RegisterPreCmdActive(preCmd,self.PreActiveCallback)
        return true
    end
    return false
end
function CommandAttack:PreActiveCallback()
    if self.delayActive > 0 then
        self:DelayCall(self.DelayDoSkill,self.delayActive)
    else
        self:DelayDoSkill()
    end
end
function CommandAttack:DelayDoSkill()
    local fighter = TBS.ActorMgr.GetActor(self.fighterId)
    fighter:DoSkill(self.skillUniqueId)
    self:AttackActive()
    --local skill = fighter:GetSkill(self.skillUniqueId)
    --[[if skill then
        skill:RegisterCallback(TBS.Config.SkillCallbackType.skillEnd,self,self.CmdEnd,true)
    end]]
    if fighter:IsLeader() and self.sourceSkillUniqueId == 0 then
        TBS.ActorMgr.LeaderTurnStart(fighter:GetGroupId())
        FireEvent(TBS.Config.Event.LeaderReleaseSkill,self.fighterId)
    end
    if fighter:IsBoss() and self.sourceSkillUniqueId == 0 then
        FireEvent(TBS.Config.Event.BossReleaseSkill,self.fighterId,self.skillId)
    end
end
function CommandAttack:AttackActive()
    self:Active()
    local fighter = TBS.ActorMgr.GetActor(self.fighterId)
    local skill = fighter:GetSkill(self.skillUniqueId)
    if not skill then
        logError(self.cid.."找不到技能unique:"..self.skillUniqueId..","..self.skillId)
        self:CmdEnd()
        return
    end
    if skill:HasEndTime() then
        skill:RegisterCallback(TBS.Config.SkillCallbackType.skillEnd,self,self.CmdEnd,true)
    else
        self:CmdEnd()
    end
end
---@class TBS.CommandDamage: TBS.CommandBase
---@field New fun():TBS.CommandDamage
local CommandDamage = TBS.CreateClass("CommandDamage","CommandBase")
function CommandDamage:OnCreate()
    TBS.CommandBase.OnCreate(self)
    self.fighterId = 0
    self.targetId = 0
    self.skillId = 0
    self.skillUniqueId = 0
    self.damage = 0
    self.damageType = 0
    --self.skillFrame = 0
end
function CommandDamage:ToString()
    local b = TBS.CommandBase.ToString(self)
    return "        [伤害]  "..b..",攻击者:"..self.fighterId..",目标:"..self.targetId..",技能id:"..self.skillId..
            ",技能唯一id:"..self.skillUniqueId..",伤害:"..self.damage..",伤害类型:"..self.damageType
end
function CommandDamage:DoExecute()
    local fighter = TBS.ActorMgr.GetActor(self.fighterId)
    if not fighter then
        logError("伤害找不到 fighter "..self.fighterId)
        return
    end
    local target = TBS.ActorMgr.GetActor(self.targetId)
    if not target then
        logError("伤害找不到 target "..self.targetId)
        return
    end
    if self.skillUniqueId > 0 then --技能触发伤害
        self.skill = fighter:GetSkill(self.skillUniqueId)
        if self.skill then
            self.skill:AddTarget(self.targetId)
            self.skill:RegisterCallback(TBS.Config.SkillCallbackType.hitBefore,self,self.DoDamage)
            self.skill:RegisterCallback(TBS.Config.SkillCallbackType.hitAfter,self,self.DoHitAfter)
            return
        else
            --logError("[伤害]找不到来源技能 "..self.skillUniqueId..","..self.skillId)
        end
    end
    --触发时间同步上个节点
    local cmd = TBS.ReportMgr.GetPreCmd(self)
    self:RegisterPreCmdActive(cmd,self.DoDamageImmediate)
end
function CommandDamage:DoDamageImmediate()
    local fighter = TBS.ActorMgr.GetActor(self.fighterId)
    local target = TBS.ActorMgr.GetActor(self.targetId)
    target:Hurt(self.damage,self.damageType,fighter,0)
    self:Active()
end
---@param context HitContext
function CommandDamage:DoDamage(context)
    if self.fighterId ~= context.casterId or self.targetId ~= context.targetId then
        return
    end
    context.eventFinish = true
    context.damage = self.damage
    context.damageType = self.damageType
    self.skill:UnRegisterCallback(TBS.Config.SkillCallbackType.hitBefore,self,self.DoDamage)
end
function CommandDamage:DoHitAfter()
    self:Active()
    self.skill:UnRegisterCallback(TBS.Config.SkillCallbackType.hitAfter,self,self.DoHitAfter)
end
---@class TBS.CommandBuff: TBS.CommandBase
---@field New fun():TBS.CommandBuff
local CommandBuff = TBS.CreateClass("CommandBuff","CommandBase")
function CommandBuff:OnCreate()
    TBS.CommandBase.OnCreate(self)
    self.fighterId = 0
    self.targetId = 0
    self.buffId = 0
    self.sourceSkill = 0
    self.sourceUniqueSkill = 0
    --local buffShow = nil; --value["4"]; 沒使用了
    self.buffServerId = 0--唯一id  向后兼容协议
    --local buffServerId = buffId;--唯一id  向后兼容协议
    self.buffOverlay = 0--buff层数(默认为1)  向后兼容协议
end
function CommandBuff:ToString()
    local b = TBS.CommandBase.ToString(self)
    local str = ""
    if self.type == 1 then
        str = ",(移除)"
    end
    return "    [BUFF]  "..b..",施法者:"..self.fighterId..",目标:"..self.targetId..
            ",buffId:"..self.buffId..",层数:"..self.buffOverlay..
            ",来源技能:"..self.sourceSkill..",来源技能唯一id:"..self.sourceUniqueSkill..str
end
function CommandBuff:DoExecute()
    local fighter = TBS.ActorMgr.GetActor(self.fighterId)
    if not fighter then
        logError("Buff找不到 fighter "..self.fighterId)
        return
    end
    local target = TBS.ActorMgr.GetActor(self.targetId)
    if not target then
        logError("Buff找不到 target "..self.targetId)
        return
    end
    if self.type == 1 then --移除
        local cmd = TBS.ReportMgr.GetPreCmd(self)
        self:RegisterPreCmdActive(cmd,self.RemoveBuffImmediate)
        return
    end
    if self.sourceUniqueSkill > 0 then
        local cfg = dataTable.getDataTableByStringId("t_collocationskillshow",self.sourceSkill)
        if not cfg then
            logError("找不到t_collocationskill "..self.sourceSkill)
            return
        end
        self.skill = fighter:GetSkill(self.sourceUniqueSkill)
        if self.skill then
            local flag = false
            if not string.IsNilOrEmpty(cfg.f_AdditionalOpportunity) then
                for k,v in pairs(cfg.f_AdditionalOpportunity) do
                    local time = v[1]
                    local _id = v[2]
                    if _id == self.buffId then
                        self:RegisterBuffCallback(time,self.buffId)
                        flag = true
                        break
                    end
                end
            end
            if flag then
                return
            end
            --logError("来源技能"..self.sourceSkill.."找不到该buff "..self.buffId)
        else
            --logError("[buff]找不到来源技能 "..self.sourceUniqueSkill..","..self.sourceSkill)
        end
    end
    --该Buff来源不明，触发时间同步上个节点
    local cmd = TBS.ReportMgr.GetPreCmd(self)
    self:RegisterPreCmdActive(cmd,self.AddBuffImmediate)
end
function CommandBuff:RemoveBuffImmediate()
    local target = TBS.ActorMgr.GetActor(self.targetId)
    target:RemoveBuff(self.buffId)
    self:Active()
    self:CmdEnd()
end
--function CommandBuff:Buff
function CommandBuff:AddBuffImmediate()
    local target = TBS.ActorMgr.GetActor(self.targetId)
    local buff = target:AddBuff(self.buffId,self.buffOverlay)
    self:Active()
    if buff and buff:HasStartEff() then
        buff:RegisterStartEffEndCallback(self,self.CmdEnd,true)
    else
        self:CmdEnd()
    end
end
function CommandBuff:RegisterBuffCallback(time,id)
    if time == -1 then
        self.skill:RegisterCallback(TBS.Config.SkillCallbackType.hitAfter,self,self.DoAddBuff,true)
    else
        self:DelayCall(self.AddBuffImmediate,time)
    end
end
---@param context BaseContext
function CommandBuff:DoAddBuff(context)
    if self.fighterId ~= context.casterId then
        return
    end
    local target = TBS.ActorMgr.GetActor(self.targetId)
    local buff = target:AddBuff(self.buffId,self.buffOverlay)
    self:Active()
    if buff and buff:HasStartEff() then
        buff:RegisterStartEffEndCallback(self,self.CmdEnd,true)
    else
        self:CmdEnd()
    end
    --self.skill:UnRegisterBeforeBuffAddCallback(self,self.DoAddBuff)
end
---@class TBS.CommandDead: TBS.CommandBase
---@field New fun():TBS.CommandDead
local CommandDead = TBS.CreateClass("CommandDead","CommandBase")
function CommandDead:OnCreate()
    TBS.CommandBase.OnCreate(self)
    self.fighterId = 0
    self.killerId = 0
end
function CommandDead:ToString()
    local b = TBS.CommandBase.ToString(self)
    return "    [死亡]  "..b..",死亡:"..self.fighterId..",击杀者:"..self.killerId
end
function CommandDead:DoExecute()
    local cmd = TBS.ReportMgr.GetPreCmd(self)
    self:RegisterPreCmdActive(cmd,self.DeadImmediate)
end
function CommandDead:DeadImmediate()
    local actor = TBS.ActorMgr.GetActor(self.fighterId)
    local killer = TBS.ActorMgr.GetActor(self.killerId)
    actor:Dead(killer)
    self:Active()
end
---@class TBS.CommandRound: TBS.CommandBase
---@field New fun():TBS.CommandRound
local CommandRound = TBS.CreateClass("CommandRound","CommandBase")
function CommandRound:OnCreate()
    TBS.CommandBase.OnCreate(self)
    self.fighterId = 0
end
function CommandRound:ToString()
    local b = TBS.CommandBase.ToString(self)
    local str
    if self:IsTurnStart() then
        str = "[回合开始]"
    elseif self:IsTurnEnd() then
        str = "[回合结束]"
    elseif self:IsBossTurn() then
        str = "[敌方回合]"
    else
        local actor = TBS.ActorMgr.GetActor(self.fighterId)
        if actor:IsLeader() then
            str = "[我方回合]第"..actor:GetGroupId().."组 "
        else
            str = "[我方回合]第"..actor:GetGroupId().."组第"..actor:GetGroupIndex().."位 "
        end
    end
    return str..b..",fighter:"..self.fighterId
end
function CommandRound:DoExecute()
    self:Active()
end
function CommandRound:IsTurnStart()
    return self.fighterId == "-1"
end
function CommandRound:IsTurnEnd()
    return self.fighterId == "-2"
end
function CommandRound:IsBossTurn()
    local actor = TBS.ActorMgr.GetBoss()
    if not actor then
        return false
    end
    return actor:GetId() == self.fighterId
end
function CommandRound:GroupTurn()
    if self:IsTurnStart() or self:IsTurnEnd() then
        return 0
    end
    local actor = TBS.ActorMgr.GetActor(self.fighterId)
    if not actor then return 0 end
    return actor.groupId
end

---@class TBS.CommandHit: TBS.CommandBase
---@field New fun():TBS.CommandHit
local CommandHit = TBS.CreateClass("CommandHit","CommandBase")
function CommandHit:OnCreate()
    TBS.CommandBase.OnCreate(self)
    self.fighterId = 0
    self.skillId = 0
    self.skillUniqueId = 0
end
function CommandHit:ToString()
    local b = TBS.CommandBase.ToString(self)
    return "[    受击]  "..b..",fighter:"..self.fighterId..",技能id:"..self.skillId..",技能唯一id:"..self.skillUniqueId
end
function CommandHit:DoExecute()
    local fighter = TBS.ActorMgr.GetActor(self.fighterId)
    if not fighter then
        logError("伤害找不到 fighter "..self.fighterId)
        return
    end
    if self.skillUniqueId > 0 then
        local skill = fighter:GetSkill(self.skillUniqueId)
        if skill then
            skill:RegisterCallback(TBS.Config.SkillCallbackType.hitBefore,self,self.DoActive,true)
            return
        end
    end
    self:Active()
end
function CommandHit:DoActive()
    self:Active()
end